#pragma once

#include "Mutex.hpp"
#include <iostream>
#include <unistd.h>
#include <vector>

const static int gcap = 5;

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = gcap)
        : _cap(cap), _ring_queue(cap), _data_sem(0), _space_sem(cap), _c_step(0), _p_step(0)
    {
    }
    void Pop(T *out) // 传址传参，将数据带出去
    {
        // 获取信号量,对于消费者，只有环形队列中有数据才可以去消费
        _data_sem.P();
        // 在获取信号量之后加锁,这样就不会影响效率；反之就是串行
        {
            LockGuard lockguard(&_c_lock);
            // P操作成功之后，消费者取数据；P操作成功就说明环形队列中不为空，即使为满，生产者会被阻塞
            // 所以生产者此时不会影响消费者
            *out = _ring_queue[_c_step++];
            _c_step %= _cap; // 环绕问题
        }
        // 消费者拿走了一个数据，数据--，空间++
        _space_sem.V();
    }
    void Enqueue(const T &in)
    {
        // 生产者向环形队列中放数据
        _space_sem.P(); // 空间有剩余申请信号量才会成功
        {
            LockGuard lockguard(&_p_lock);
            // P操作成功就意味着环形队列中的数据不为满，而为空，消费者是阻塞的，所以消费者无法影响生产者发数据
            _ring_queue[_p_step++] = in;
            _p_step %= _cap;
        }
        _data_sem.V();
    }
    ~RingQueue()
    {
    }

private:
    std::vector<T> _ring_queue;
    int _cap;

    // 需要两个信号量，一个记录空间剩余量，一个记录数据量
    // 并且需要两个指针来记录消费者、生产者所要访问的空间
    Sem _space_sem;
    Sem _data_sem;

    int _p_step;
    int _c_step;

    Mutex _p_lock;
    Mutex _c_lock;
};